/*
 * @(#)FileMonitor.java	1.0 Mar 8, 2008
 *
 *	The MIT License
 *
 *	Copyright (c) 2008 Malachi de AElfweald <malachid@gmail.com>
 *
 *	Permission is hereby granted, free of charge, to any person obtaining a copy
 *	of this software and associated documentation files (the "Software"), to deal
 *	in the Software without restriction, including without limitation the rights
 *	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the Software is
 *	furnished to do so, subject to the following conditions:
 *
 *	The above copyright notice and this permission notice shall be included in
 *	all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *	THE SOFTWARE.
 */
package org.eoti.io.file;

import org.eoti.io.file.event.FileChangedListener;
import org.eoti.util.TimeUtil;
import static org.eoti.util.TimeUtil.Millisecond;
import static org.eoti.util.TimeUtil.Second;

import java.util.*;
import java.io.File;

public class FileMonitor
{
	protected HashMap<String,FileMonitorTask> tasks;
	protected Timer timer;

	protected static FileMonitor singleton;
	protected static FileMonitor getInstance()
	{
		if(singleton == null)
			singleton = new FileMonitor();

		return singleton;
	}
	protected FileMonitor()
	{
		tasks = new HashMap<String,FileMonitorTask>();
		timer = new Timer("FileMonitorTimer", true); //daemon
	}

	protected String getKey(FileChangedListener listener, File file)
	{
		return listener.hashCode() + ":" + file.getAbsolutePath();
	}

	protected void add(FileChangedListener listener, File file, long interval, TimeUtil.Unit timeUnit)
	{
		long ms = TimeUtil.Unit.Millisecond.convertFrom(interval, timeUnit);

		// ensure it is not there already
		remove(listener, file);
		FileMonitorTask task = new FileMonitorTask(listener, file);
		tasks.put(getKey(listener,file), task);
		timer.schedule(task, ms, ms);
	}

	protected void remove(org.eoti.io.file.event.FileChangedListener listener, File file)
	{
		FileMonitorTask task = tasks.remove(getKey(listener,file));
		if(task != null) task.cancel();
	}

	protected static void fireFileChangedEvent(FileChangedListener listener, File file, long oldTimestamp, long newTimestamp)
	{
		listener.fileChanged(new org.eoti.io.file.event.FileChangedEvent(file, oldTimestamp, newTimestamp));
	}

	public static void addFileChangedListener(FileChangedListener listener, File file, long intervalMs)
	{
		addFileChangedListener(listener, file, intervalMs, Millisecond);
	}

	public static void addFileChangedListener(FileChangedListener listener, File file, long interval, TimeUtil.Unit timeUnit)
	{
		getInstance().add(listener, file, interval, timeUnit);
	}

	public static void removeFileChangedListener(org.eoti.io.file.event.FileChangedListener listener, File file)
	{
		getInstance().remove(listener, file);
	}

	class FileMonitorTask
	extends TimerTask
	{
		protected org.eoti.io.file.event.FileChangedListener listener;
		protected File file;
		protected long oldTimestamp, newTimestamp;

		public FileMonitorTask(FileChangedListener listener, File file)
		{
			super();
			this.listener = listener;
			this.file = file;
			this.oldTimestamp = file.lastModified();
		}

		public void run()
		{
			newTimestamp = file.lastModified();
			if(newTimestamp != oldTimestamp)
			{
				FileMonitor.fireFileChangedEvent(listener, file, oldTimestamp, newTimestamp);
				oldTimestamp = newTimestamp;
			}
		}
	}

	public static void main(String[] args)
	{
		if(args.length == 0)
		{
			System.out.println("Command-Line USAGE: java org.eoti.io.file.FileMonitor fileName");
			System.exit(0);
		}
		try{
	    	FileMonitor.addFileChangedListener(
	    		new FileChangedListener()
	    		{
	    			public void fileChanged(org.eoti.io.file.event.FileChangedEvent event)
	    			{
	    				switch(event.getEventType())
	    				{
	    					case Deleted:
		    					System.out.println( "FILE WAS DELETED: " + event.getFile().getAbsolutePath() + " since " + (new Date(event.getOldTimestamp())) );
		    					break;
		    				case Created:
								System.out.println( "FILE WAS CREATED: " + event.getFile().getAbsolutePath() + " @" + (new Date(event.getNewTimestamp())) );
		    					break;
		    				case Modified:
								System.out.println( "FILE WAS MODIFIED: " + event.getFile().getAbsolutePath() + " @" + (new Date(event.getNewTimestamp())) );
		    					break;
	    				}
	    			}
	    		}, new File(args[0]),
	    		1,
	    		Second
	    	);

	    	System.out.println("Change listener instantiated with 1 second monitoring...");
	    	System.out.println("Waiting for 30 seconds. Go ahead and change the file...");
	    	Second.sleep(30);
	    	System.out.println("DONE");
	    }catch(Exception e){
	    	System.out.println("Exception: " + e.getMessage());
	    	e.printStackTrace();
	    }
	}
}